package DiskManager;

import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;

public class Disk {
    public static String DISK_FILE_PATH = "disk.dat";
    // 块数量
    public int AMOUNT_OF_BLOCKS = 64;
    // 块大小
    public static int SIZE_OF_BLOCK = 64;
    // 位示图起始字节
    public static int BITMAP_START_BYTE_INDEX = 2;
    // FAT
    public byte[] FAT = null;
    public int next_empty_block_index = -1;
    public int amountOfAllocatedBlocks = 0;

    public Disk() {
        readDisk();
        updateNextEmptyBlockIndex();
    }

    // 磁盘读取
    public void readDisk() {
        File diskFile = new File(DISK_FILE_PATH);
        // 检查文件是否存在，若不存在则创建
        if (!diskFile.exists()) {
            try {
                diskFile.createNewFile();
                initDisk(diskFile);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // 读取文件
            try {
                RandomAccessFile raf = new RandomAccessFile(diskFile, "r");
                // 读取块大小
                byte[] size_reader = new byte[1];
                raf.read(size_reader);
                raf.close();
                SIZE_OF_BLOCK = size_reader[0];
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            // 计算块数量
            AMOUNT_OF_BLOCKS = (int) diskFile.length() / SIZE_OF_BLOCK;
        }
        // 读取FAT
        FAT = readFAT();
        // 更新块信息
        for (int i = AMOUNT_OF_BLOCKS - 1; i >= 0; --i) {
            if (FAT[i] != 0)
                ++amountOfAllocatedBlocks;
            else
                next_empty_block_index = i;
        }
    }

    // 初始化磁盘
    public void initDisk(File diskFile) {
        byte[] init_byte = new byte[AMOUNT_OF_BLOCKS * SIZE_OF_BLOCK];
        init_byte[0] = (byte) SIZE_OF_BLOCK;
        init_byte[1] = -1;
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(diskFile, "rw");
            randomAccessFile.write(init_byte);
            randomAccessFile.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Disk init complete.");
    }

    // 读取FAT
    public byte[] readFAT() {
        byte[] FAT = readBlock(0);
        FAT[0] = -1;
        // FAT[1] = -1;
        return FAT;
    }

    // 更新空闲块索引
    public void updateNextEmptyBlockIndex() {
        if (next_empty_block_index != -1 && FAT[next_empty_block_index] == 0 || isFull())
            return;
        int new_index = next_empty_block_index + 1;
        while (new_index != next_empty_block_index) {
            if (FAT[new_index] == 0) {
                next_empty_block_index = new_index;
                return;
            }
            new_index = (new_index + 1) % AMOUNT_OF_BLOCKS;
        }
    }

    // 读取块
    public byte[] readBlock(int blockIndex) throws IndexOutOfBoundsException {
        byte[] content = new byte[SIZE_OF_BLOCK];
        if (blockIndex >= AMOUNT_OF_BLOCKS || blockIndex < 0)
            throw new IndexOutOfBoundsException();
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(new File(DISK_FILE_PATH), "r");
            randomAccessFile.seek(blockIndex * SIZE_OF_BLOCK);
            randomAccessFile.read(content);
            randomAccessFile.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;
    }
    // 按块修改内容
    public void updateBlockContent(byte[] new_content, int block_i) {
        if (new_content.length != SIZE_OF_BLOCK || block_i < 0 || block_i >= AMOUNT_OF_BLOCKS)
            return;
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(new File(DISK_FILE_PATH), "rw");
            randomAccessFile.seek(block_i * SIZE_OF_BLOCK);
            randomAccessFile.write(new_content);
            randomAccessFile.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public boolean isFull() {
        return amountOfAllocatedBlocks == AMOUNT_OF_BLOCKS;
    }

    // 连续读取块
    public String readFilebyIndex(int index)
    {
        String content = new String();
        if (FAT[index] != 0) {
            do {
                content += new String(readBlock(index));
                index = FAT[index];
            } while (index > 0);
        }
        return content;
    }

    // 分配空间
    public Integer requestforSpace(byte[] b) {
        int len = b.length;
        int count = 0;
        // 检查是否有足够的空间存放
        if (len > (AMOUNT_OF_BLOCKS - amountOfAllocatedBlocks) * SIZE_OF_BLOCK || len == 0)
            return -1;
        int first_index = next_empty_block_index;
        while (len > SIZE_OF_BLOCK) {
            byte[] pt = new byte[SIZE_OF_BLOCK];
            System.arraycopy(b, count * SIZE_OF_BLOCK, pt, 0, SIZE_OF_BLOCK);
            updateBlockContent(pt, next_empty_block_index);
            int cur_index = next_empty_block_index;
            FAT[cur_index] = -1;
            updateNextEmptyBlockIndex();
            FAT[cur_index] = (byte) next_empty_block_index;
            ++count;
            len -= SIZE_OF_BLOCK;
        }
        byte[] pt = new byte[SIZE_OF_BLOCK];
        System.arraycopy(b, count * SIZE_OF_BLOCK, pt, 0, b.length - count * SIZE_OF_BLOCK);
        updateBlockContent(pt, next_empty_block_index);
        int cur_index = next_empty_block_index;
        FAT[cur_index] = -1;
        updateNextEmptyBlockIndex();
        ++count;
        amountOfAllocatedBlocks -= count;
        writeFAT();
        return first_index;
    }

    public Integer updateFilebyBlockIndex(byte[] b, int blockIndex) {
        int len = b.length;
        int count = 0;
        // 回收空间
        int indexp = blockIndex;
        while (FAT[indexp] != -1)
        {
            int curp = indexp;
            indexp = FAT[indexp];
            FAT[curp] = 0;
        }
        FAT[indexp] = 0;
        next_empty_block_index = blockIndex;

        // 检查是否有足够的空间存放
        if (len > (AMOUNT_OF_BLOCKS - amountOfAllocatedBlocks) * SIZE_OF_BLOCK || len == 0)
            return -1;
        int first_index = next_empty_block_index;
        while (len > SIZE_OF_BLOCK) {
            byte[] pt = new byte[SIZE_OF_BLOCK];
            System.arraycopy(b, count * SIZE_OF_BLOCK, pt, 0, SIZE_OF_BLOCK);
            updateBlockContent(pt, next_empty_block_index);
            int cur_index = next_empty_block_index;
            FAT[cur_index] = -1;
            updateNextEmptyBlockIndex();
            FAT[cur_index] = (byte) next_empty_block_index;
            ++count;
            len -= SIZE_OF_BLOCK;
        }
        byte[] pt = new byte[SIZE_OF_BLOCK];
        System.arraycopy(b, count * SIZE_OF_BLOCK, pt, 0, b.length - count * SIZE_OF_BLOCK);
        updateBlockContent(pt, next_empty_block_index);
        int cur_index = next_empty_block_index;
        FAT[cur_index] = -1;
        updateNextEmptyBlockIndex();
        ++count;
        amountOfAllocatedBlocks -= count;
        writeFAT();
        return first_index;
    }

    public void writeFAT() {
        byte[] newFAT = FAT;
        FAT[0] = (byte) SIZE_OF_BLOCK;
        updateBlockContent(newFAT, 0);
    }
}
